package br.com.menosehmais.framework.persistence;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.UUID;

import javax.persistence.EntityListeners;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.PrePersist;
import javax.persistence.Transient;
import org.apache.log4j.Logger;

/**
 * 
 * @author Seabra
 * @author Quintino
 */
@EntityListeners({ DomainObject.UUIDGeneratorListener.class, EntityListener.class })
@MappedSuperclass
public abstract class DomainObject implements Comparable<DomainObject>, Serializable {

    public static final long serialVersionUID = 1L;
    protected static final Logger LOG = Logger.getLogger(DomainObject.class);

    @Id
    protected String id = null;

    public DomainObject() {
    }

    public DomainObject(String id) {
        this.id = id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    @Override
    public boolean equals(Object that) {
        if (that != null && that.getClass().equals(this.getClass())) {
            DomainObject domainObject = (DomainObject) that;
            if (this == domainObject) {
                return true;
            } else if ((domainObject.id != null) && (this.id != null)) {
                return domainObject.id.equals(this.id);
            } else {
                Field fields[] = this.getClass().getDeclaredFields();
                for (Field f : fields) {
                    String fieldName = f.getName();

                    if (f.getType().isEnum()) {
                        f.setAccessible(true);
                        try {
                            if (!f.get(that).equals(f.get(this))) {
                                return false;
                            }
                        } catch (Exception ex) {
                            LOG.debug(ex.getMessage(), ex);
                            return false;
                        }
                    } else if (fieldName != null && !fieldName.equals("id") && !f.isAnnotationPresent(Transient.class)) {
                        StringBuilder methodName = new StringBuilder("get");
                        methodName.append(Character.toUpperCase(fieldName.charAt(0)));
                        if (methodName.length() > 1) {
                            methodName.append(fieldName.substring(1));
                        }
                        try {
                            Method method = this.getClass().getDeclaredMethod(methodName.toString(), (Class<?>[])null);
                            if (!method.isAnnotationPresent(Transient.class)) {
                                method.setAccessible(true);
                                Object thisAttribute = method.invoke(this, (Object)null);
                                Object thatAttribute = method.invoke(that, (Object)null);

                                if (thisAttribute != thatAttribute && thisAttribute != null && !thisAttribute.equals(thatAttribute)) {
                                    return false;
                                }
                            }
                        } catch (Exception ex) {
                            LOG.debug(ex.getMessage(), ex);
                            return false;
                        }
                    }
                }
                return true;
            }
        }
        return false;
    }

    @Override
    public int hashCode() {
        if (id != null) {
            return id.hashCode();
        }
        return super.hashCode();
    }

    @Override
    public int compareTo(DomainObject o) {
        UUID thisUUID = UUID.fromString(id);
        UUID thatUUID = UUID.fromString(o.id);

        return (int) (thisUUID.timestamp() - thatUUID.timestamp());
    }

    // TODO
    /*
     * http://www.mojavelinux.com/blog/archives/2006/06/
     * hibernate_get_out_of_my_pojo/
     * http://www.anzaan.com/2010/06/serializing-cglib
     * -enhanced-proxy-into-json-using-xstream/
     */

    public static class UUIDGeneratorListener {
        @PrePersist
        public void onPrePersist(DomainObject o) {
            o.setId(UUID.randomUUID().toString());
        }
    }
}
